package com.joysuch.wwyt.bp.service.impl;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.collect.UnmodifiableIterator;
import com.joysuch.wwyt.bp.bean.investigate.BpInvestigateItemDto;
import com.joysuch.wwyt.bp.bean.investigate.BpSafeTableBean;
import com.joysuch.wwyt.bp.bean.investigate.BpSafeTableItemBean;
import com.joysuch.wwyt.bp.bean.investigate.BpSafeTableQueryBean;
import com.joysuch.wwyt.bp.entity.investigate.*;
import com.joysuch.wwyt.bp.enums.SafeTableSourceTypes;
import com.joysuch.wwyt.bp.repository.*;
import com.joysuch.wwyt.bp.service.BpSafeTableService;
import com.joysuch.wwyt.core.common.ajax.ResultBean;
import com.joysuch.wwyt.core.common.util.PageDataUtils;
import com.joysuch.wwyt.core.repository.BaseUserDao;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.util.Assert;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.validation.constraints.NotEmpty;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author ZhangDong
 * @Date 2020/12/2 13:29
 */
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class BpSafeTableServiceImpl implements BpSafeTableService {

    @Autowired
    private BpSafeTableDao dao;
    @Autowired
    private BpSafeTableItemDao itemDao;
    @Autowired
    private BpSafeTableTypeDao typeDao;
    @Autowired
    private BpDepartDao departDao;
    @Autowired
    private BaseUserDao userDao;
    @Autowired
    private BpInvestigatePlanRepository planDao;

    @Override
    public ResultBean save(BpSafeTableBean bean) {
        Long id = bean.getId();
        String tableName = bean.getTableName().trim();
        List<BpSafeTableItemBean> itemList = bean.getItemList();
        //检查项目不为空验证
        if (CollectionUtils.isEmpty(itemList)) {
            return ResultBean.fail(101, "请选择至少一个检查项目");
        }
        //检查类型必须为2级的验证
        BpSafeTableType tableType = typeDao.findById(bean.getTypeId()).orElseThrow(() -> new IllegalArgumentException("此类型不存在"));
        if (tableType.getParentId() == 0) {
            return ResultBean.fail(101, "需要选择2级类型再新增");
        }
        Integer exist = dao.countByTableName(tableName);
        BpSafeTable table;
        if (id == null) {
            if (exist != null) {
                return ResultBean.fail(101, "此名称已存在");
            }
            table = new BpSafeTable();
        } else {
            table = dao.findById(id).orElseThrow(() -> new IllegalArgumentException("此数据不存在"));
            if (!tableName.equals(table.getTableName())) {
                if (exist != null) {
                    return ResultBean.fail(101, "此名称已存在");
                }
            }
        }
        BeanUtils.copyProperties(bean, table);
        BpSafeTable saveTable = dao.save(table);
        Long tableId = saveTable.getId();
        //项目
        Set<Long> hasIdSet = itemList.stream().filter(r -> r.getId() != null).map(r -> r.getId()).collect(Collectors.toSet());
        if (id != null) {
            //只有不为空的情况下才会存在需要删除的子项
            Set<Long> existIdSet = itemDao.findIdBySafeTableId(id).stream().collect(Collectors.toSet());
            Sets.SetView<Long> difference = Sets.difference(existIdSet, hasIdSet);
            UnmodifiableIterator<Long> iterator = difference.iterator();
            while (iterator.hasNext()) {
                itemDao.deleteById(iterator.next());
            }
        }
        for (BpSafeTableItemBean itemBean : itemList) {
            Long itemId = itemBean.getId();
            BpSafeTableItem item;
            if (itemId == null) {
                item = new BpSafeTableItem();
            } else {
                item = itemDao.findById(itemId).orElseThrow(() -> new IllegalArgumentException("此数据不存在"));
            }
            BeanUtils.copyProperties(itemBean, item);
            item.setSafeTableId(tableId);
            itemDao.save(item);
        }
        return ResultBean.success(tableId);
    }

    @Override
    public ResultBean delete(Long id) {
        //有排查计划关联不给删
        int count = planDao.countBySafeTableId(id);
        if (count > 0) {
            return ResultBean.fail(101, "请先解除和排查计划的绑定");
        }
        //删除检查项目
        List<Long> itemIdList = itemDao.findIdBySafeTableId(id);
        if (!CollectionUtils.isEmpty(itemIdList)) {
            itemDao.deleteAllByIdIn(itemIdList);
        }
        //删除主数据
        dao.deleteById(id);
        return ResultBean.defaultSuccessResult();
    }

    @Override
    public ResultBean page(BpSafeTableQueryBean bean) {
        Pageable pageable = PageRequest.of(bean.getPage(), bean.getSize());
        pageable = PageDataUtils.addOrderByDesc(pageable, "id");
        String tableName = bean.getTableName();
        //父类型需取所有子类型
        Long typeId = bean.getTypeId();
        List<Long> typeIdList = Lists.newArrayList();
        if (typeId != null) {
            BpSafeTableType tableType = typeDao.findById(typeId).orElseThrow(() -> new IllegalArgumentException("此类型不存在"));
            Long parentId = tableType.getParentId();
            if (parentId == 0) {
                List<Long> childIdList = typeDao.findByParentId(typeId).stream().map(r -> r.getId()).collect(Collectors.toList());
                typeIdList.addAll(childIdList);
            } else {
                typeIdList.add(typeId);
            }
            if (CollectionUtils.isEmpty(typeIdList)) {
                return ResultBean.pageData(typeIdList, 0);
            }
        }

        Specification<BpSafeTable> specification = (Root<BpSafeTable> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (!CollectionUtils.isEmpty(typeIdList)) {
                CriteriaBuilder.In<Object> in = cb.in(root.get("typeId"));
                for (Long aLong : typeIdList) {
                    in.value(aLong);
                }
                predicates.add(in);
            }
            if (tableName != null) {
                predicates.add(cb.like(root.get("tableName"), "%" + tableName + "%"));
            }
            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        Page<BpSafeTable> page = dao.findAll(specification, pageable);
        covert(page);
        List<BpSafeTable> content = page.getContent();
        for (BpSafeTable table : content) {
            orderItem(table);
        }
        return ResultBean.pageData(page.getContent(), page.getTotalElements());
    }

    private void orderItem(BpSafeTable table) {
        Set<BpSafeTableItem> itemList = table.getItemList();
        if (CollectionUtils.isEmpty(itemList)) {
            return;
        }
        ArrayList<BpSafeTableItem> bpSafeTableItems = Lists.newArrayList(itemList);
        List<BpSafeTableItem> collect = bpSafeTableItems.stream().sorted(Comparator
                .comparing(BpSafeTableItem::getId).reversed()).collect(Collectors.toList());
        table.setItemWebList(collect);
    }

    @Override
    public ResultBean get(Long id) {
        Assert.notNull(id, "id cannot be null");
        BpSafeTable table = dao.findById(id).orElseThrow(() -> new IllegalArgumentException("此数据不存在"));
        orderItem(table);
        return ResultBean.success(table);
    }

    private void covert(Page<BpSafeTable> page) {
        List<BpSafeTable> list = page.getContent();
        List<Long> typeIdList = list.stream().map(r -> r.getTypeId()).distinct().collect(Collectors.toList());
        Map<Long, String> typeMap = typeDao.findAllById(typeIdList).stream().collect(Collectors.toMap(r -> r.getId(), r -> r.getTypeName()));
        List<Long> departIdList = list.stream().map(r -> r.getDutyDepartId()).distinct().collect(Collectors.toList());
        Map<Long, String> departMap = departDao.findAllById(departIdList).stream().collect(Collectors.toMap(r -> r.getId(), r -> r.getName()));
        List<Long> userIdList = list.stream().map(r -> r.getCreateBy()).filter(Objects::nonNull).distinct().collect(Collectors.toList());
        Map<Long, String> userMap = userDao.findAllById(userIdList).stream().collect(Collectors.toMap(r -> r.getId(), r -> r.getRealName()));
        for (BpSafeTable table : list) {
            table.setCreateUserName(userMap.getOrDefault(table.getCreateBy(), ""));
            table.setDepartName(departMap.getOrDefault(table.getDutyDepartId(), ""));
            table.setTypeName(typeMap.getOrDefault(table.getTypeId(), ""));
        }
    }

    /**
     * 管控措施添加的项目修改时需要同步: 只做修改，不做删除
     */
    @Override
    public void itemSave(BpInvestigateItemDto dto) {
        List<BpSafeTableItem> list = itemDao.findBySourceAndSourceId(SafeTableSourceTypes.CONTROL_ACTION, dto.getId());
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        for (BpSafeTableItem item : list) {
            item.setItem(dto.getItem());
            item.setContent(dto.getContent());
            item.setStandard(dto.getStandard());
        }
        itemDao.saveAll(list);
        List<Long> itemIdList = list.stream().map(r -> r.getId()).collect(Collectors.toList());
        log.info("管控措施项目修改同步, itemId:" + itemIdList);
    }

}
